﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Microsoft.Reputation.Tests.FrontEndCommon
{
    public partial class UrfReputationObject
    {
        [Flags]
        public enum BloomFlags
        {
            NoBloom         = 0x00 << 0,
            ValidBloom      = 0x01 << 0,
            InvalidBloom    = 0x01 << 1,
            FullBloom       = InvalidBloom | ValidBloom
        }

        public byte[] Pack(uint[] expirationArray)
        {
            int serverIndex = expirationArray.Select((s, i) => new { s, i }).Where(exp => exp.s == this.ServerExpirationMinutes).Select(exp => exp.i).FirstOrDefault();
            int clientIndex = expirationArray.Select((s, i) => new { s, i }).Where(exp => exp.s == this.ClientExpirationMinutes).Select(exp => exp.i).FirstOrDefault();
            this.Expiration = (byte)(serverIndex << 4 | clientIndex);
            return Pack();
        }

        public byte[] Pack()
        {
            return ArrangeMembers().SelectMany(bytes => bytes).ToArray();
        }

        private IEnumerable<byte> ExpandMember(byte member)
        {
            yield return member;
        }

        private static byte CalculateFlags(out bool needBloom, byte[] bloom)
        {
            needBloom = false;
            // Flag logic
            if (bloom == null || bloom.Length != 33 || bloom.Skip(1).All(b => b == 0x00))
            {
                return (byte)BloomFlags.NoBloom;
            }
            else if (bloom.Skip(1).All(b => b == 0x11))
            {
                return (byte)BloomFlags.FullBloom;
            }
            else if (bloom.First() >= 0x01 && bloom.First() <= 0x20)
            {
                needBloom = true;
                return (byte)BloomFlags.ValidBloom;
            }
            else
            {
                return (byte)BloomFlags.InvalidBloom;
            }
        }

        private IEnumerable<IEnumerable<byte>> ArrangeMembers()
        {
            bool needBloom;

            yield return ExpandMember(Type);
            yield return Hash;
            yield return BitConverter.GetBytes(Version).Reverse().ToArray();
            yield return ExpandMember((byte)Reputation.Length);
            yield return ExpandMember(CalculateFlags(out needBloom, Bloom));
            yield return ExpandMember(Expiration);
            foreach (ReputationCategory category in Reputation)
            {
                yield return category.Pack();
            }
            if (needBloom)
            {
                yield return Bloom;
            }
        }
    }
}
